/*
 * @(#)GenDoc.java	1.8 06/10/10
 *
 * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 * 
 */

package sun.tools.javazic;

import	java.io.IOException;
import	java.io.File;
import	java.io.FileReader;
import	java.io.FileWriter;
import	java.io.BufferedReader;
import	java.io.BufferedWriter;
import	java.util.ArrayList;
import	java.util.Calendar;
import	java.util.Date;
import	java.util.HashMap;
import	java.util.Iterator;
import	java.util.LinkedList;
import	java.util.Set;
import	java.util.StringTokenizer;
import	java.util.TreeMap;
import	java.util.TreeSet;
import	sun.util.calendar.CalendarDate;
import	sun.util.calendar.Gregorian;
import	sun.util.calendar.ZoneInfoFile;

/**
 * <code>GenDoc</code> is one of back-end classes of javazic, and generates 
 * index.html and other html files which prints the detailed time zone
 * information for each zone.
 */
class GenDoc extends BackEnd {

    private static final String docDir = "doc";

    private static final String header1 =
	"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Frameset//EN\"" +
	"\"http://www.w3.org/TR/REC-html40/frameset.dtd\">\n" +
	"<HTML>\n<HEAD>\n<!-- Generated by javazic on ";
    private static final String header2 =
	"-->\n<TITLE>\n" +
	"Java 2 Platform Standard Edition - TimeZone information based on ";
    private static final String header3 =
	"-->\n<TITLE>\n" +
	"Java 2 Platform Standard Edition  TimeZone - ";
    private static final String header4 =
	"</TITLE>\n" +
	"</HEAD>\n\n";

    private static final String body1 =
	"<BODY BGCOLOR=\"white\">\n";
    private static final String body2 =
	"</BODY>\n";

    private static final String footer =
	"</HTML>\n";


    // list of time zone name and zonefile name/real time zone name
    //   e.g.
    //      key (String)       : value (String)
    //      "America/Denver"   : "America/Denver.html" (real time zone)
    //      "America/Shiprock" : "America/Denver"      (alias)
    TreeMap timezoneList = new TreeMap();

    // list of time zone's display name and time zone name
    //   e.g.
    //      key (String)                : value (String)
    //      "Tokyo, Asia"               : "Asia/Tokyo"
    //      "Marengo, Indiana, America" : "America/Indiana/Marengo"
    //          (aliases included)
    TreeMap displayNameList = new TreeMap();

    // list of top level regions
    //   e.g.
    //      key (String) : value (String)
    //      "America"    : "America.html"
    //          (including entries in America/Indiana/, America/Kentucky/, ...) 
    TreeMap regionList = new TreeMap(); 

    // mapping list from zone name to latitude & longitude
    //   This list is generated from zone.tab.
    //   e.g.
    //      key (String) : value (LatitudeAndLongitude object)
    //      "Asia/Tokyo" : latitude=35.3916, longitude=13.9444
    //          (aliases not included)
    HashMap mapList = null;

    /**
     * Generates HTML document for each zone.
     * @param Timezone
     * @return 0 if no errors, or 1 if error occurred.
     */
    int processZoneinfo(Timezone tz) {
	try {
	    int size;
	    int index;
	    String outputDir = Main.getOutputDir();
	    String zonename = tz.getName();
	    String zonefile = ZoneInfoFile.getFileName(zonename) + ".html";
	    ArrayList stz = tz.getLastRules();

	    timezoneList.put(zonename, zonefile);
	    displayNameList.put(transform(zonename), zonename);

	    /* If outputDir doesn't end with file-separator, adds it. */
	    if (!outputDir.endsWith(File.separator)) {
		outputDir += File.separatorChar;	
	    }
	    outputDir += docDir + File.separatorChar;	

	    index = zonename.indexOf('/');
	    if (index != -1) {
		regionList.put(zonename.substring(0, index),
			       zonename.substring(0, index) + ".html");
	    }

	    /* If zonefile includes file-separator, it's treated as part of
	     * pathname. And make directory if necessary.
	     */
	    index = zonefile.lastIndexOf('/');
	    if (index != -1) {
		zonefile.replace('/', File.separatorChar);
		outputDir += zonefile.substring(0, index+1);
	    }
	    File outD = new File(outputDir);
	    outD.mkdirs();

	    /* If mapfile is available, add a link to the appropriate map */
	    if ((mapList == null) && (Main.getMapFile() != null)) {
		FileReader fr = new FileReader(Main.getMapFile());
		BufferedReader in = new BufferedReader(fr);
		mapList = new HashMap();
		String line;
		while ((line = in.readLine()) != null) {
		    // skip blank and comment lines
		    if (line.length() == 0 || line.charAt(0) == '#') {
			continue;
		    }
		    StringTokenizer tokens = new StringTokenizer(line);
		    String token = tokens.nextToken();	/* We don't use the first token. */
		    token = tokens.nextToken();
		    LatitudeAndLongitude location = new LatitudeAndLongitude(token);
		    token = tokens.nextToken();
		    mapList.put(token, location);
		}
		in.close();
	    }

	    /* Open zoneinfo file to write. */
	    FileWriter fw = new FileWriter(outputDir + zonefile.substring(index+1));
	    BufferedWriter out = new BufferedWriter(fw);

	    out.write(header1 + new Date() + header3 + zonename + header4);
	    out.write(body1 + "<FONT size=\"+2\"><B>" + zonename + "</B></FONT>");
	    LatitudeAndLongitude location = (LatitudeAndLongitude)mapList.get(zonename);
	    if (location != null) {
		float lat = location.getLatitude();
		float lon = location.getLongitude();
		out.write("&nbsp;&nbsp;&nbsp;" +
			  "<A HREF=\"http://mapweb.parc.xerox.com/map/color=1/border=1/ht=30/lat=" +
			  lat + "/lon=" + lon + "/mark=" + lat + "%2C" + lon +
			  "/\">[map]</A>");
	    }
	    out.write("\n<P>\n");

	    ArrayList zone = tz.getZones();
	    ArrayList rule = tz.getRules();
	    if (rule != null && zone != null) {
		out.write("<TABLE BORDER=\"0\" WIDTH=\"100%\" CELLPADDING=\"1\" CELLSPACING=\"0\">\n" +
			  "<TR>\n" +
			  "<TD BGCOLOR=\"#EEEEFF\" WIDTH=\"50%\" ALIGN=\"CENTER\"><BR>" +
			  "<A HREF=\"#Rule\">Rule</A><BR></TD>\n" +
			  "<TD BGCOLOR=\"#EEEEFF\" WIDTH=\"50%\" ALIGN=\"CENTER\">" +
			  "<A HREF=\"#Zone\"><BR>Zone<BR></A></TD>\n" +
			  "</TR>\n</TABLE>\n");
	    }

	    /* Output Rule records. */
	    if (rule != null) {
		size = rule.size();
		out.write("<P>\n<A NAME=\"Rule\">" +
			  "<FONT SIZE=\"+1\"><B>Rules</B></FONT></A>\n" +
			  "<TABLE BORDER=\"1\" WIDTH=\"100%\" CELLPADDING=\"3\" CELLSPACING=\"0\">\n" +
			  "<TR BGCOLOR=\"#CCCCFF\">\n" +
			  "<TD>NAME</TD><TD>FROM</TD><TD>TO</TD><TD>TYPE</TD>" +
			  "<TD>IN</TD><TD>ON</TD><TD>AT</TD><TD>SAVE</TD>" +
			  "<TD>LETTER/S</TD><TD>NOTES</TD>\n</TR>\n");
		for (int i = 0; i < size; i++) {
		    out.write("<TR BGCOLOR=\"#FFFFFF\">\n");
		    StringTokenizer st = new StringTokenizer(((RuleRec)rule.get(i)).getLine());
		    String s;
		    if (st.hasMoreTokens()) {	/* RULE - truncated */
			st.nextToken();
		    }
		    if (st.hasMoreTokens()) {	/* NAME */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* FROM */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* TO */
			s = st.nextToken();
			if (s.equals("max")) {
			    out.write("<TD><FONT COLOR=\"red\">" + s + "</FONT></TD>");
			} else {
			    out.write("<TD>" + s + "</TD>");
			}
		    }
		    if (st.hasMoreTokens()) {	/* TYPE */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* IN */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* ON */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* AT */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* SAVE */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* LETTER/S */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* NOTES */
			s = st.nextToken();
			while (st.hasMoreTokens()) {
			    s += " " + st.nextToken();
			}
			index = s.indexOf('#');
			out.write("<TD>" + s.substring(index+1) + "</TD>\n");
		    } else {
			out.write("<TD>&nbsp;</TD>\n");
		    }
		    out.write("</TR>\n");
		}
		out.write("</TABLE>\n<P>&nbsp;<P>\n");
	    }

	    /* Output Zone records. */
	    if (zone != null) {
		size = zone.size();
		out.write("<P>\n<A NAME=\"Zone\">" +
			  "<FONT SIZE=\"+1\"><B>Zones</B></FONT></A>\n" +
			  "<TABLE BORDER=\"1\" WIDTH=\"100%\" CELLPADDING=\"3\" CELLSPACING=\"0\">\n" +
			  "<TR BGCOLOR=\"#CCCCFF\">\n<TD>GMTOFF</TD>" +
			  "<TD>RULES</TD><TD>FORMAT</TD><TD>UNTIL</TD>" +
			  "<TD>NOTES</TD>\n</TR>\n");
		for (int i = 0; i < size; i++) {
		    out.write("<TR>\n");
		    StringTokenizer st = new StringTokenizer(((ZoneRec)zone.get(i)).getLine());
		    String s = st.nextToken();
		    if (s.equals("Zone")) {	/* NAME */
			s = st.nextToken();
			s = st.nextToken();
		    }
		    out.write("<TD>" + s + "</TD>");	/* GMTOFFSET */
		    if (st.hasMoreTokens()) {	/* RULES */
			out.write("<TD>" + st.nextToken() + "</TD>");
		    }
		    if (st.hasMoreTokens()) {	/* FORMAT */
			s = st.nextToken();
			index = s.indexOf('#');
			if (index != -1) {
			    if (index != 0) {
				out.write("<TD>" + s.substring(0, index-1) +
					  "</TD>");	/* FORMAT */
				s = s.substring(index+1);
			    } else {
				out.write("<TD>&nbsp;</TD>");	/* FORMAT */
			    }
			    while (st.hasMoreTokens()) {
				s += " " + st.nextToken();
			    }
			    out.write("<TD>&nbsp;</TD>");	/* UNTIL */
			    out.write("<TD>" + s + "</TD>\n</TR>\n");	/* NOTES */
			    continue;
			} else {
			    out.write("<TD>" + s + "</TD>");	/* FORMAT */
			}
		    }

		    if (st.hasMoreTokens()) {	/* UNTIL */
			s = st.nextToken();
			while (st.hasMoreTokens()) {
			    s += " " + st.nextToken();
			}
			index = s.indexOf('#');
			if (index != -1) {
			    if (index != 0) {
				out.write("<TD>" + s.substring(0, index-1) +
					  "</TD>");	/* UNTIL */
			    } else {
				out.write("<TD>&nbsp;</TD>");	/* UNTIL */
			    }
			    out.write("<TD>" + s.substring(index+1) +
				      "</TD>\n");	/* NOTES */
			} else {
			    out.write("<TD>" + s + "</TD>");	/* UNTIL */
			    out.write("<TD>&nbsp;</TD>\n");	/* NOTES */
			}
		    } else {
			out.write("<TD>&nbsp;</TD>");		/* UNTIL */
			out.write("<TD>&nbsp;</TD>\n");		/* NOTES */
		    }
		    out.write("</TR>\n");
		}
		out.write("</TABLE>\n");
	    }
	    out.write(body2 + footer);

	    out.close();
	    fw.close();
	} catch(IOException e) {
	    Main.panic("IO error: "+e.getMessage());
	    return 1;
	}

	return 0;
    }

    /**
     * Generates index.html and other top-level frame files.
     * @param Mappings
     * @return 0 if no errors, or 1 if error occurred.
     */
    int generateSrc(Mappings map) {
	try {
	    int len;
	    Object o[];
	    String outputDir = Main.getOutputDir();
	    FileWriter fw1, fw2;
	    BufferedWriter out1, out2;

	    /* Whether rawOffsetIndex list exists or not. */
	    LinkedList roi = map.getRawOffsetsIndex();
	    if (roi == null) {
		Main.panic("Data not exist. (rawOffsetsIndex)");
		return 1;
	    }
	    int roi_size = roi.size();	
	    LinkedList roit = map.getRawOffsetsIndexTable();
	    if (roit == null || roit.size() != roi_size) {
		Main.panic("Data not exist. (rawOffsetsIndexTable) Otherwise, Invalid size");
		return 1;
	    }

	    /* Whether alias list exists or not. */
	    TreeMap a = map.getAliases();
	    if (a == null) {
		Main.panic("Data not exist. (aliases)");
		return 1;
	    }

	    timezoneList.putAll(a);

	    /* If outputDir doesn't end with file-separator, adds it. */
	    if (!outputDir.endsWith(File.separator)) {
		outputDir += File.separatorChar;
	    }
	    outputDir += docDir + File.separatorChar;	

	    File outD = new File(outputDir);
	    outD.mkdirs();

	    /* Creates index.html */
	    fw1 = new FileWriter(outputDir + "index.html", false);
	    out1 = new BufferedWriter(fw1);

	    out1.write(header1 + new Date() + header2 + Main.getVersionName() +
		       header4 +
		       "<FRAMESET cols=\"20%,80%\">\n" +
		       "<FRAMESET rows=\"30%,70%\">\n" +
		       "<FRAME src=\"overview-frame.html\" name=\"TimeZoneListFrame\">\n" +
		       "<FRAME src=\"allTimeZone-frame1.html\" name=\"allTimeZoneFrame\">\n" +
		       "</FRAMESET>" +
		       "<FRAME src=\"overview-summary.html\" name=\"rightFrame\">\n" +
		       "</FRAMESET>\n" +
		       "<NOFRAMES>\n" +
		       "<H2>\nFrame Alert\n</H2>\n\n" +
		       "<P>\n\n" +
		       "This document is designed to be viewed using the frames feature. If you see this\n" +
		       "message, you are using a non-frame-capable web client.\n" +
		       "<BR>\n" +
		       "Link to<A HREF=\"overview-summary.html\">Non-frame version.</A>\n" + 
		       "</NOFRAMES>\n" + footer);

	    out1.close();
	    fw1.close();


	    /* Creates overview-frame.html */
	    fw1 = new FileWriter(outputDir + "overview-frame.html", false);
	    out1 = new BufferedWriter(fw1);

	    out1.write(header1 + new Date() + header2 + Main.getVersionName() +
		       header4 + body1 +
		       "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n<TR>\n" +
		       "<TD NOWRAP><FONT size=\"+1\">\n" +
		       "<B>Java<sup><font size=-2>TM</font></sup>&nbsp;2&nbsp;Platform<br>Std.&nbsp;Ed.</B></FONT></TD>\n" +
		       "</TR>\n</TABLE>\n\n" +
		       "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n<TR>\n<TD NOWRAP>" +
		       "<P>\n<FONT size=\"+1\">\nAll Time Zones Sorted By:</FONT>\n<BR>\n" +
		       "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame1.html\" TARGET=\"allTimeZoneFrame\">GMT offsets</A></FONT>\n<BR>\n" +
		       "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame2.html\" TARGET=\"allTimeZoneFrame\">Zone names</A></FONT>\n<BR>" +
		       "&nbsp;&nbsp;<A HREF=\"allTimeZone-frame3.html\" TARGET=\"allTimeZoneFrame\">City names</A></FONT>\n" +
		       "<P>\n<FONT size=\"+1\">\nContinents and Oceans</FONT>\n<BR>\n");

	    o = regionList.keySet().toArray();
	    len = regionList.size();
	    Object tzo[] = timezoneList.keySet().toArray();
	    int tzlen = timezoneList.size();
	    for (int i = 0; i < len; i++) {
		Object regionKey = o[i];
		out1.write("&nbsp;&nbsp;<A HREF=\"" + regionList.get(regionKey) +
			   "\" TARGET=\"allTimeZoneFrame\">" + regionKey +
			   "</A><BR>\n");

		fw2 = new FileWriter(outputDir + regionList.get(regionKey),
				     false);
		out2 = new BufferedWriter(fw2);

		out2.write(header1 + new Date() + header3 + regionKey +
			   header4 + body1 + "<FONT size=\"+1\"><B>" +
			   regionKey + "</B></FONT>\n<BR>\n<TABLE>\n<TR>\n<TD>");

		boolean found = false;
		for (int j = 0; j < tzlen; j++) {
		    String timezoneKey = (String)tzo[j];
		    int regionIndex = timezoneKey.indexOf('/');
		    if (regionIndex == -1 ||
			!regionKey.equals(timezoneKey.substring(0, regionIndex))) {
			if (found) {
			    break;
			} else {
			    continue;
			}
		    } 

		    found = true;
		    if (a.containsKey(timezoneKey)) {
			Object realName = a.get(timezoneKey);
			while (a.containsKey(realName)) {
			    realName = a.get(realName);
			}
			out2.write(timezoneKey +
				   " (alias for " + "<A HREF=\"" +
				   timezoneList.get(realName) +
				   "\" TARGET=\"rightFrame\">" +
				   realName + "</A>)");
		    } else {
			out2.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
				   "\" TARGET=\"rightFrame\">" + timezoneKey +
				   "</A>");
		    }
		    out2.write("<BR>\n");
		}
		out2.write("</TD>\n</TR>\n</TABLE>\n" + body2 + footer);

		out2.close();
		fw2.close();
	    }
	    out1.write("</FONT></TD>\n</TR></TABLE>\n" + body2 + footer);

	    out1.close();
	    fw1.close();


	    /* Creates allTimeZone-frame1.html (Sorted by GMT offsets) */
	    fw1 = new FileWriter(outputDir + "allTimeZone-frame1.html", false);
	    out1 = new BufferedWriter(fw1);

	    out1.write(header1 + new Date() + header2 + Main.getVersionName() +
		       header4 + body1 +
		       "<FONT size=\"+1\"><B>Sorted by GMT offsets</B></FONT>\n" +
		       "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
		       "<TR>\n<TD NOWRAP>\n");
	    for (int i = 0; i < roi_size; i++) {
		TreeSet perRO = (TreeSet)roit.get(i);
		Iterator keys = perRO.iterator();
		while (keys.hasNext()) {
		    Object timezoneKey = keys.next();
		    out1.write("<TR>\n<TD><FONT SIZE=\"-1\">(" +
			       Time.toGMTFormat(roi.get(i).toString()) +
			       ")</FONT></TD>\n<TD>");

		    if (a.containsKey(timezoneKey)) {
			Object realName = a.get(timezoneKey);
			while (a.containsKey(realName)) {
			    realName = a.get(realName);
			}
			out1.write(timezoneKey +
				   " (alias for " + "<A HREF=\"" +
				   timezoneList.get(realName) +
				   "\" TARGET=\"rightFrame\">" + realName +
				   "</A>)");
		    } else {
			out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
				   "\" TARGET=\"rightFrame\">" + timezoneKey +
				   "</A>");
		    }
		    out1.write("</TD>\n</TR>\n");
		}
	    }
	    out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);

	    out1.close();
	    fw1.close();


	    /* Creates allTimeZone-frame2.html (Sorted by zone names) */
	    fw1 = new FileWriter(outputDir + "allTimeZone-frame2.html", false);
	    out1 = new BufferedWriter(fw1);

	    out1.write(header1 + new Date() + header2 + Main.getVersionName() +
		       header4 + body1 +
		       "<FONT size=\"+1\"><B>Sorted by zone names</B></FONT>\n" +
		       "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
		       "<TR>\n<TD NOWRAP>\n");
	    o = timezoneList.keySet().toArray();
	    len = timezoneList.size();
	    for (int i = 0; i < len; i++) {
		Object timezoneKey = o[i];
		if (a.containsKey(timezoneKey)) {
		    Object realName = a.get(timezoneKey);
		    while (a.containsKey(realName)) {
			realName = a.get(realName);
		    }
		    out1.write(timezoneKey +
			       " (alias for " +
		    	       "<A HREF=\"" + timezoneList.get(realName) +
			       "\" TARGET=\"rightFrame\">" + realName +
			       "</A>)");
		} else {
		    out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
			       "\" TARGET=\"rightFrame\">" + timezoneKey +
			       "</A>");
		}
		out1.write("<BR> \n");
	    }
	    out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);

	    out1.close();
	    fw1.close();

	    /* Creates allTimeZone-frame3.html (Sorted by city names) */
	    fw1 = new FileWriter(outputDir + "allTimeZone-frame3.html", false);
	    out1 = new BufferedWriter(fw1);

	    out1.write(header1 + new Date() + header2 + Main.getVersionName() +
		       header4 + body1 +
		       "<FONT size=\"+1\"><B>Sorted by city names</B></FONT>\n" +
		       "<BR>\n\n" + "<TABLE BORDER=\"0\" WIDTH=\"100%\">\n" +
		       "<TR>\n<TD NOWRAP>\n");

	    Set aliasSet = a.keySet();
	    len = aliasSet.size();
	    Object aliasNames[] = aliasSet.toArray();
	    for (int i = 0; i < len; i++) {
		displayNameList.put(transform((String)aliasNames[i]), aliasNames[i]);
	    }

	    o = displayNameList.keySet().toArray();
	    len = displayNameList.size();
	    for (int i = 0; i < len; i++) {
		Object displayName = o[i];
		Object timezoneKey = displayNameList.get(o[i]);
		if (a.containsKey(timezoneKey)) {
		    Object realName = a.get(timezoneKey);
		    while (a.containsKey(realName)) {
			realName = a.get(realName);
		    }
		    out1.write(displayName +
			       " (alias for " +
		    	       "<A HREF=\"" + timezoneList.get(realName) +
			       "\" TARGET=\"rightFrame\">" + realName +
			       "</A>)");
		} else {
		    out1.write("<A HREF=\"" + timezoneList.get(timezoneKey) +
			       "\" TARGET=\"rightFrame\">" + displayName +
			       "</A>");
		}
		out1.write("<BR> \n");
	    }

	    out1.write("</FONT></TD>\n</TR>\n</TABLE>\n" + body2 + footer);

	    out1.close();
	    fw1.close();

	    /* Creates overview-summary.html */
	    fw1 = new FileWriter(outputDir + "overview-summary.html", false);
	    out1 = new BufferedWriter(fw1);

	    out1.write(header1 + new Date() + header2 + Main.getVersionName() +
		       header4 + body1 +
		       "This is the list of time zones generated from <B>" +
		       Main.getVersionName() + "</B> for Java 2 Platform, " +
		       "Standard Edition. The source code can be obtained " +
		       "from ftp site <a href=\"ftp://elsie.nci.nih.gov/pub/\">" +
		       "ftp://elsie.nci.nih.gov/pub/</a>. A total of <B>" +
		       len + "</B> time zones and aliases are supported " +
		       "in this edition.\n<BR>\n" +
		       "(This site keeps only the latest release.)\nFor the " +
		       "format of rules and zones, refer to the zic " +
		       "(zoneinfo compiler) man page on " +
		       "<a href=\"http://docs.sun.com/ab2/coll.40.6/REFMAN1M/@Ab2PageView/266070?DwebQuery=zic&oqt=zic&Ab2Lang=C&Ab2Enc=iso-8859-1\">Solaris</a> " +
		       "or Linux.\n<p>\nNote that this time zone data is not " +
		       "a public interface of the Java run-time. No " +
		       "applications should rely on the time zone data on " +
		       "this web page. Time zone names and time zone data " +
		       "may change without any prior notice.\n" +
		       body2 + footer);

	    out1.close();
	    fw1.close();
	} catch(IOException e) {
	    Main.panic("IO error: "+e.getMessage());
	    return 1;
	}

	return 0;
    }

    String transform(String s) {
	int index = s.lastIndexOf("/");

	/* If the string doesn't include any delimiter, return */
	if (index == -1) {
	    return s;
	}

	int lastIndex = index;
	String str = s.substring(index+1);
	do {
	    index = s.substring(0, lastIndex).lastIndexOf('/');
	    str += ", " + s.substring(index+1, lastIndex);
	    lastIndex = index;
	} while (index > -1);

	return str;
    }
}

class LatitudeAndLongitude {
    private float latitude;
    private float longitude;

    LatitudeAndLongitude(String s) {
	try {
	    if (!s.startsWith("+") && !s.startsWith("-")) {
		Main.warning("Wrong latitude&longitude data: " + s);
		return;
	    }

	    int index;
	    if (((index = s.lastIndexOf("+")) <= 0) &&
		((index = s.lastIndexOf("-")) <= 0)) {
		Main.warning("Wrong latitude&longitude data: " + s);
		return;
	    }

	    latitude = ((s.startsWith("+")) ? 1 : -1) * Float.parseFloat(s.substring(1, index));
	    if (index > 5) {
		latitude /= 10000;
	    } else {
		latitude /= 100;
	    }

	    longitude = ((s.charAt(index) == '+') ? 1 : -1) * Float.parseFloat(s.substring(index+1));
	    if ((s.length()-index) > 6) {
		longitude /= 10000;
	    } else {
		longitude /= 100;
	    }
	} catch(Exception e) {
	    Main.warning("LatitudeAndLongitude() Parse error: " + s);
	}
    }

    float getLatitude() {
	return latitude;
    }

    float getLongitude() {
	return longitude;
    }
}
